/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */

#ifndef ECARX_VHAL_CORE_PROPERTY_IPROPERTY_H
#define ECARX_VHAL_CORE_PROPERTY_IPROPERTY_H

#include <cstdint>
#include <functional>
#include <map>
#include <set>
#include <vector>

#include "vhal_core/Common.h"
#include "vhal_core/persist/DID.h"
#include "vhal_core/persist/PlatformProfile.h"
namespace ecarx::vhal::core::property {

class Area;

class IProperty {
  protected:
    // 定时任务函数
    using TimerFn = std::function<void()>;

  private:
    const int32_t mPropertyId;
    const std::string mPropName;
    WorkPhase mWorkPhase{WorkPhase::INIT_PHASE};
    // 该Property包含的Area
    std::map<int32_t, Area*> mAreas;
    // config配置
    VehiclePropConfig config;
    // 该Property的定时任务单元
    std::map<uint32_t, TimerFn> timerTasks;

    std::function<void(const VehiclePropValue&,CommitMode)> mReportValueCallback = nullptr;
    persist::DID* mDID = nullptr;
    persist::PlatformProfile* mPlatformProfile = nullptr;

  public:
    IProperty(const int32_t propertyId, const char* propName, VehiclePropertyChangeMode chanMode,
              VehiclePropertyAccess access);
    virtual ~IProperty();

    void sampleRate(const float minSampleRate, const float maxSampleRate);
    void mixedTypeConfig(bool oneStr, bool oneBool, bool oneInt, int32_t sizeInt, bool oneLong,
                         int32_t sizeLong, bool oneFloat, int32_t sizeFloat, int32_t sizeByte);

    void configArray(std::vector<int32_t>& array);
    int32_t getPropertyId() const { return mPropertyId; }

    void addTimeTask(uint32_t msgId, TimerFn&& fn) {
        this->timerTasks.insert(std::pair<int32_t, TimerFn>(msgId, fn));
    }

    void addArea(Area* area);

    Area* getArea(int32_t areaId) const;

    const std::string getName() const { return mPropName; }

    VehiclePropConfig& getConfig() { return config; }

    std::map<int32_t, Area*>& getAreas() { return mAreas; }

    bool invalidProperty();

    WorkPhase getWorkPhase();

    void setWorkPhase(WorkPhase phase);

    void executeTimer(uint32_t msgId) const;

    void registerWritePropValueListener(std::function<void(const VehiclePropValue&,CommitMode)>&& fn);

    void writeToStore(const VehiclePropValue& value,CommitMode mode) const;

    bool setFunctionValue(const VehiclePropValue& value);

    void setDID(persist::DID* did);
    persist::DID* getDID();
    void setPlatformProfile(persist::PlatformProfile* profile);
    persist::PlatformProfile* getPlatformProfile();
};

class Area {
    // 信号监听任务
    using SignalChangedCallback = std::function<void(Area*, int64_t)>;
    // 初始值函数
    using InitValueFn = std::function<void(Area*)>;
    // 恢复函数
    using RecoveryValueFn = std::function<void(Area*)>;
    // 状态执行函数
    using StatusFn = std::function<PropertyStatus(Area*)>;
    // set功能函数
    using ExecutableSetFn = std::function<void(const VehiclePropValue&)>;
    // 处理其他Property值变化函数
    using PropValueChangeFn = std::function<void(const VehiclePropValue& value)>;

    struct SignalCallback {
        int64_t mToken;
        SignalChangedCallback _callback;
        void operator()(int64_t token, int64_t timestamp, Area* area) {
            if (token != mToken) {
                mToken = token;
                _callback(area, timestamp);
            }
        }
    };

    IProperty* mProperty;
    const int32_t areaId;
    std::string key;

    PropertyStatus mStatus;
    // PropValue
    VehiclePropValue mPropValue;
    RecoveryValueFn mRecoveryFn = nullptr;
    ExecutableSetFn mAreaSetFn = nullptr;
    StatusFn mStatusFn = nullptr;
    std::map<int32_t, std::vector<SignalCallback>> mSignalCallback;
    std::map<RecordId, PropValueChangeFn> mPropChangeCallback;

    std::set<int32_t> signals;
    std::vector<RecordId> properties;
    VehicleAreaConfig areaConfig;
    PersistType mPersistType = PersistType::Normal;
    CommitMode mCommitMode = CommitMode::ValueChanged;
    // 恢复以及触发onSetFunction的时间戳
    int64_t mOnSetTimestamp = 0;
    // 记录当前任务是否恢复
    bool isRecovery = false;

  public:
    Area(IProperty* function, int32_t areaId,
         VehiclePropertyStatus status = VehiclePropertyStatus::UNAVAILABLE);
    ~Area() {}

    PropertyStatus exeSignalChanged(int32_t signalId, int64_t token, int64_t timestamp);
    bool exeSetFunction(const VehiclePropValue&);
    bool exeTimerFn(uint32_t);
    PropertyStatus exeStatusFn();
    bool tryRecoveryTask(bool force);

    VehiclePropValue& getValue() { return mPropValue; }
    const std::set<int32_t>& getOwnSignals() const { return signals; }
    const std::vector<RecordId>& getDependentProperties() const { return properties; }
    bool hasDependent() const { return !properties.empty(); }
    int32_t getAreaId() const { return areaId; }
    PropertyStatus getStatus() const { return mStatus; }
    const std::map<RecordId, PropValueChangeFn>& getCareProps() { return mPropChangeCallback; }

    VehicleAreaConfig& getAreaConfig() { return areaConfig; }

    void setSetTask(ExecutableSetFn&& fn) { this->mAreaSetFn = fn; }
    void setRecoveryFn(RecoveryValueFn&& fn);
    void setInitValueFn(InitValueFn&& fn);
    void setStatusFn(StatusFn&& _callback);
    void setPersistType(PersistType type) { mPersistType = type; }
    void setPropValueCommitMode(CommitMode mode) { mCommitMode=mode; }

    void registerSignalCallback(int32_t signal, SignalChangedCallback&& _callback);

    void registerPropValueChangeCallback(int32_t propId, int32_t areaId, PropValueChangeFn&& fn);

    void addProperties(int32_t propertyId, int32_t areaId) {
        areaId = isGlobalProp(propertyId) ? 0 : areaId;
        if (propertyId == mProperty->getPropertyId() && areaId == getAreaId()) {
            return;
        }
        this->properties.emplace_back(RecordId{.prop = propertyId, .area = areaId});
    }

    IProperty* getIProperty() const { return mProperty; }

    const std::string& getKey() const;

    void recoveried(bool recoveried);

    bool isRecoveried() const { return isRecovery; }

    bool isSupportProfile() { return mPersistType == PersistType::Profile; }
    bool isSupportRefrence() { return mPersistType == PersistType::Preference; }
    bool isSupportDID() { return mPersistType == PersistType::DID; }
    bool isSupportPersist() { return mPersistType != PersistType::Normal; }

    bool runnableBySignal(int64_t timestamp);

    persist::Properties* getPersist();

    void updateStatus(PropertyStatus status);
    void setBoolValue(bool value, bool persist = false);
    void setInt32Value(int32_t value, bool persist = false);
    void setInt32Values(const std::vector<int32_t>& value, bool persist = false);
    void setInt64Value(int64_t value, bool persist = false);
    void setInt64Values(const std::vector<int64_t>& value, bool persist = false);
    void setFloatValue(float value, bool persist = false);
    void setFloatValues(const std::vector<float>& value, bool persist = false);
    void setStringValue(const std::string& value, bool persist = false);
    void setBytes(const std::vector<uint8_t>& value, bool persist = false);
    void setMixedValue(const MixedValue& value, bool persist = false);
};

}  // namespace ecarx::vhal::core::property

#endif
